package com.example.demo2.common.check;

import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;

/**
 * 类似guava的Preconditions. 并在抛出异常时携带errorCode和fieldError等信息，以便于直接转换为api请求结果。
 */
public class Check implements Cloneable {
    private static Log log = LogFactory.get();
    private int code = 0;

    // for serializable
    @SuppressWarnings("unused")
    private Check() {
    }

    private Check(Check from) {
        this.code = from.code;
    }

    protected Check(int code) {
        this.code = code;
    }

    @Override
    protected Check clone() {
        return new Check(this);
    }

    public static Check error(int code) {
        return new Check(code);
    }

    public void check(boolean condition) {
        if (!condition) {
            throw new CheckException(newErrorBuilder().build());
        }
    }

    /**
     * if condition is false throw CheckException
     */
    public void check(boolean condition, ErrorBuilderVisitor visitor) {
        if (!condition) {
            throw new CheckException(toError(visitor));
        }
    }

    public <T> T checkNotNull(T obj) {
        if (obj == null) {
            throw new CheckException(newErrorBuilder().build());
        }
        return obj;
    }

    public <T> T checkNotNull(T obj, ErrorBuilderVisitor visitor) {
        if (obj == null) {
            throw new CheckException(toError(visitor));
        }
        return obj;
    }

    /**
     * long ageLong = check.checkNoFailed(() -> Long.parseLong(age), e ->
     * e.message("参数age不是数字"));
     */
    public <T> T checkNotFailed(Supplier<T> action, ErrorBuilderVisitor visitor) {
        try {
            return action.get();
        } catch (Throwable e) {
            log.error(e, "Check.checkNotFailed");
            throw new CheckException(
                    newErrorBuilder().cause(e).debugMessage(e.getMessage()).visit(visitor).build());
        }
    }

    /**
     * long ageLong = check.checkNoFailed(() -> Long.parseLong(age));
     */
    public <T> T checkNotFailed(Supplier<T> action) {
        try {
            return action.get();
        } catch (Throwable e) {
            log.error(e, "Check.checkNotFailed");
            throw new CheckException(
                    newErrorBuilder().cause(e).debugMessage(e.getMessage()).build());
        }
    }

    public void checkNotFailed(Action0 action, ErrorBuilderVisitor visitor) {
        try {
            action.call();
        } catch (Throwable e) {
            log.error(e, "Check.checkNotFailed");
            throw new CheckException(
                    newErrorBuilder().cause(e).debugMessage(e.getMessage()).visit(visitor).build());
        }
    }

    public void checkNotFailed(Action0 action) {
        try {
            action.call();
        } catch (Throwable e) {
            log.error(e, "Check.checkNotFailed");
            throw new CheckException(
                    newErrorBuilder().cause(e).debugMessage(e.getMessage()).build());
        }
    }

    public void fail(ErrorBuilderVisitor visitor) {
        throw new CheckException(toError(visitor));
    }

    public void fail() {
        throw new CheckException(newErrorBuilder().build());
    }

    public CheckException toException(ErrorBuilderVisitor visitor) {
        return new CheckException(toError(visitor));
    }

    public CheckError toError(ErrorBuilderVisitor visitor) {
        return newErrorBuilder().visit(visitor).build();
    }

    public int getCode() {
        return code;
    }

    protected CheckError.Builder newErrorBuilder() {
        return CheckError.builder().code(code);
    }

    public interface ErrorBuilderVisitor {
        void call(CheckError.Builder builder);
    }

    @FunctionalInterface
    public interface Action0 {
        public abstract void call() throws Exception;
    }

    @FunctionalInterface
    public interface Supplier<T> {
        T get() throws Exception;
    }

}
